/*
 *    Copyright © OpenAtom Foundation.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package com.inspur.edp.cef.spi.entity.info.propertyinfo;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.inspur.edp.cef.api.manager.serialize.CefSerializeContext;
import com.inspur.edp.cef.api.manager.serialize.JsonFormatType;
import com.inspur.edp.cef.api.manager.serialize.NestedSerializeContext;
import com.inspur.edp.cef.entity.accessor.base.IAccessor;
import com.inspur.edp.cef.entity.changeset.IChangeDetail;
import com.inspur.edp.cef.entity.changeset.ValueObjModifyChangeDetail;
import com.inspur.edp.cef.entity.entity.AssoInfoBase;
import com.inspur.edp.cef.entity.entity.FieldType;
import com.inspur.edp.cef.entity.entity.ICefData;
import com.inspur.edp.cef.spi.entity.AssociationInfo;
import com.inspur.edp.cef.spi.jsonser.abstractcefchange.BefDateSerUtil;
import com.inspur.edp.cef.spi.jsonser.base.StringUtils;
import com.inspur.edp.cef.spi.jsonser.util.SerializerUtil;

import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

public class UdtPropertyInfo extends BasePropertyInfo {

    protected String udtConfigId;
    protected final NestedSerializeContext context;

    public UdtPropertyInfo(String udtConfigId, boolean enableStdTimeFormat) {
        this.udtConfigId = udtConfigId;
        context = new NestedSerializeContext();
        context.setEnableStdTimeFormat(enableStdTimeFormat);
    }

    public String getUdtConfigId() {
        return udtConfigId;
    }

    public void setUdtConfigId(String udtConfigId) {
        this.udtConfigId = udtConfigId;
    }

    public final boolean getEnableStdTimeFormat() {
        if (context == null)
            return false;
        return context.getEnableStdTimeFormat();
    }

    protected NestedSerializeContext getNestedSerContext() {
        return context;
    }

    @Override
    public void write(JsonGenerator writer, String propertyName, Object value, SerializerProvider serializer, CefSerializeContext serContext) {
        context.setBigNumber(serContext.isBigNumber());
        dealEnableStdTimeFormat(serContext);
        if (serContext.getJsonFormatType() == JsonFormatType.Tiled) {
            //todo 关联udt还没有处理
            if (this instanceof SimpleUdtPropertyInfo && !(this instanceof SimpleAssoUdtPropertyInfo)) {//单值UDT
                writeTiledSingleUDT(writer, (ICefData) value, propertyName);
            } else if (this instanceof SimpleAssoUdtPropertyInfo) {
                writeTiledAssoUDT((SimpleAssoUdtPropertyInfo) this, writer, serializer, serContext, (ICefData) value, propertyName);
            } else if (this instanceof ComplexUdtPropertyInfo) {
                writeTiledComplexUDT(writer, (ICefData) value, propertyName, (ComplexUdtPropertyInfo) this);
            } else {
                SerializerUtil.writeNestedValue(writer, serializer, udtConfigId, (ICefData) value, propertyName, context);
            }
        } else {
            if(value != null && !(value instanceof ICefData)){
                throw new RuntimeException("属性" + propertyName + "的值"+value+"无法转换为ICefData类型");
            }
            SerializerUtil.writeNestedValue(writer, serializer, udtConfigId, (ICefData) value, propertyName, context);
        }
    }

    private void writeTiledSingleUDT(JsonGenerator jsonGenerator, ICefData value, String propertyName) {
        if (value == null) {
            SerializerUtil.writeBaseType(jsonGenerator, value, propertyName, null);
            return;
        }
        for (String prop : value.getPropertyNames()) {
            if (prop == null)//不知道哪里给CefDataBase 的getPropertyNames() 里面，add了一个null
                continue;
            Object rawValue = value.getValue(prop);
            try {
                jsonGenerator.writeFieldName(StringUtils.toCamelCase(propertyName));
                if (rawValue instanceof Enum) {
                    SerializerUtil.writeBasicTypeProp(jsonGenerator, rawValue.toString());
                } else {
                    SerializerUtil.writeBasicTypeProp(jsonGenerator, rawValue);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private void writeTiledComplexUDT(JsonGenerator jsonGenerator, ICefData value, String propertyName, ComplexUdtPropertyInfo complexUdtPropertyInfo) {
        //todo 后续考虑value为null平铺显示问题
        if (value == null)
            return;
        for (String prop : value.getPropertyNames()) {
            Object rawValue = value.getValue(prop);
            try {

                jsonGenerator.writeFieldName(StringUtils.toCamelCase(propertyName + "_" + prop));
                DataTypePropertyInfo dataTypePropertyInfo = complexUdtPropertyInfo.getPropertyInfos().get(prop);
                if (dataTypePropertyInfo != null) {
                    if (dataTypePropertyInfo.getFieldType() == FieldType.Date)
                        jsonGenerator.writeString(BefDateSerUtil.getInstance().writeDate((Date) value));
                    if (dataTypePropertyInfo.getFieldType() == FieldType.DateTime)
                        jsonGenerator.writeString(BefDateSerUtil.getInstance().writeDateTime((Date) value));
                    else {
                        SerializerUtil.writeBasicTypeProp(jsonGenerator, rawValue);
                    }
                } else {
                    if ("LastChangedOn".equals(prop) || "CreatedOn".equals(prop))
                        jsonGenerator.writeString(BefDateSerUtil.getInstance().writeDateTime((Date) rawValue));
                    else
                        SerializerUtil.writeBasicTypeProp(jsonGenerator, rawValue);
                }
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

    private void writeTiledComplexUDT(JsonGenerator jsonGenerator, ValueObjModifyChangeDetail changeDetail, String propertyName) {
        //todo 后续考虑value为null平铺显示问题
        if (changeDetail == null)
            return;
        for (String prop : changeDetail.getPropertyChanges().keySet()) {
            Object rawValue = changeDetail.getPropertyChanges().get(prop);
            try {
                jsonGenerator.writeFieldName(StringUtils.toCamelCase(propertyName + "_" + prop));
                SerializerUtil.writeBasicTypeProp(jsonGenerator, rawValue);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }


    private void writeTiledAssoUDT(SimpleAssoUdtPropertyInfo assoUdtPropertyInfo, JsonGenerator writer, SerializerProvider serializerProvider, CefSerializeContext cefSerializeContext, ICefData value, String propertyName) {
        if (value == null) {
            SerializerUtil.writeBaseType(writer, value, propertyName, serializerProvider);
            return;
        }

        AssoInfoBase asso = (AssoInfoBase) value.getValue(value.getPropertyNames().get(0));
        AssociationInfo associationInfo = assoUdtPropertyInfo.getAssoInfo().getAssociationInfo();
        String keyName = this.getUdtConfigId().substring(this.getUdtConfigId().lastIndexOf('.') + 1);//associationInfo.getPrivateTargetColumn();
        if (asso == null) {
            SerializerUtil.writeBaseType(writer, null, associationInfo.getPrivateTargetColumn(), serializerProvider);
            for (Map.Entry<String, DataTypePropertyInfo> refInfoItem : associationInfo.getRefPropInfos().entrySet()) {
//                SerializerUtil.writeBaseType(writer, null, refInfoItem.getKey().substring(refInfoItem.getKey().indexOf('_') + 1), serializerProvider);
                refInfoItem.getValue().write("", writer, null, serializerProvider, cefSerializeContext);
            }
            return;
        }

        //SerializerUtil.writeString(writer, asso.getValue(keyName), associationInfo.getPrivateTargetColumn() + "_" + keyName, serializerProvider);

        SerializerUtil.writeString(writer, asso.getValue(keyName), associationInfo.getPrivateTargetColumn(), serializerProvider);
//        associationInfo.getPrivateTargetColumn() + "_" + keyName
//        CefSerializeContext refSerContext = new CefSerializeContext() {{
//            setEnableStdTimeFormat(serContext.getEnableStdTimeFormat());
//        }};
        for (Map.Entry<String, DataTypePropertyInfo> refInfoItem : associationInfo.getRefPropInfos().entrySet()) {
            refInfoItem.getValue().write("", writer, asso.getValue(refInfoItem.getKey().substring(refInfoItem.getKey().indexOf('_') + 1)), serializerProvider, cefSerializeContext);
        }
//        writeExtAssoInfo(writer, asso, serializer, serContext);
    }

    @Override
    public void writeChange(JsonGenerator writer, String propertyName, Object value, SerializerProvider serializer, CefSerializeContext serContext) {
        context.setBigNumber(serContext.isBigNumber());
        dealEnableStdTimeFormat(serContext);
        if(!(value instanceof IChangeDetail)){
            return;
        }
        if (serContext.getJsonFormatType() == JsonFormatType.Tiled) {
            //todo 关联udt还没有处理
            ValueObjModifyChangeDetail changeDetail = (ValueObjModifyChangeDetail) value;
            if (this instanceof SimpleUdtPropertyInfo && !(this instanceof SimpleAssoUdtPropertyInfo)) {//单值UDT
                writeTiledSingleUDT(writer, changeDetail.getData(), propertyName);
            } else if (this instanceof SimpleAssoUdtPropertyInfo) {
                writeTiledAssoUDT((SimpleAssoUdtPropertyInfo) this, writer, serializer, serContext, changeDetail.getData(), propertyName);
            } else if (this instanceof ComplexUdtPropertyInfo) {
                writeTiledComplexUDT(writer, changeDetail, propertyName);
            } else {
                SerializerUtil.writeNestedValue(writer, serializer, udtConfigId, (ICefData) value, propertyName, context);
            }
        } else {
            SerializerUtil.writeNestedChange(writer, serializer, udtConfigId, (IChangeDetail) value, propertyName, context);
        }
    }

    @Override
    public Object read(JsonParser reader, String propertyName, DeserializationContext serializer, CefSerializeContext serContext) {
        dealEnableStdTimeFormat(serContext);
        return SerializerUtil.readNestedValue(udtConfigId, reader, serializer, context);
    }

    @Override
    public Object read(JsonNode node, String propertyName, CefSerializeContext serContext) {
        return null;
    }

    @Override
    public Object readChange(JsonParser reader, String propertyName, DeserializationContext serializer, CefSerializeContext serContext) {
        dealEnableStdTimeFormat(serContext);
        if (serContext.getJsonFormatType() == JsonFormatType.Tiled) {
            context.setJsonFormatType(JsonFormatType.Tiled);
            return readTiledChange(reader, propertyName, serializer, context);
        }
        return SerializerUtil.readNestedChange(udtConfigId, reader, serializer, context);
    }

    @Override
    public Object readChange(JsonNode node, String propertyName, CefSerializeContext serContext) {
        return null;
    }

    protected Object readTiledChange(
            JsonParser reader,
            String propertyName,
            DeserializationContext serializer,
            NestedSerializeContext serContext) {

        JsonNode node = null;
        HashMap<String, JsonNode> nodes = new HashMap();
        try {
            node = reader.readValueAsTree();
            nodes.put(propertyName, node);
            return SerializerUtil.readNestedChange(udtConfigId, nodes, null, serContext);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }

    @Override
    public void setValue(Object data, String propertyName, Object value, CefSerializeContext serContext) {

    }

    private void dealEnableStdTimeFormat(CefSerializeContext serializeContext) {
        if (serializeContext != null) {
            context.setEnableStdTimeFormat(serializeContext.getEnableStdTimeFormat());
            context.setJsonFormatType(serializeContext.getJsonFormatType());
        }

    }

    @Override
    public Object createValue() {
        return SerializerUtil.createUdt(udtConfigId);
    }

    @Override
    public Object createChange() {
        return new ValueObjModifyChangeDetail();
    }


    public String getPropValue(Object value) {
        if (value == null) {
            return "";
        }
        return SerializerUtil.getNestedValue(udtConfigId, (ICefData) value);
    }
}
